/**
 * @file   Array.h
 * @author Wang Heyu <hywang@wukong>
 * @date   Mon Sep 27 21:16:10 2021
 * 
 * @brief A teaching demo for Array.
 * 
 * 
 */

#ifndef __CRAZYFISH_ARRAY__
#define __CRAZYFISH_ARRAY__

#include <iostream>
#include <cstdlib>

/**
 * An implementation for Array.
 * 
 */
template <typename T>
class Array;

template <typename T>
class Array
{
private:
    T* elements = nullptr;
    int size = 0;
    void copyMemory(const T* _s, T* _d, int _n);

public:
    Array()
    {
    };

    /** 
     * Gether all exceptions, and give warning, but will not stop the
     * program abnormally.
     * 
     * @param _err Error code.
     */
    void warning(int _err) const;
    
    ~Array()
    {
	clearMemory();
    };
    
    /** 
     * Clear the memory allocated to elements. And empty the Array.
     * 
     */
    void clearMemory()
    {
	if (elements != nullptr)
	    delete [] elements;
	elements = nullptr;
	size = 0;
    }
    
    Array(const Array<T>& _obj);
    
    const Array<T>& operator=(const Array<T>& _obj);

    /** 
     * Empty means the size is zero. It NOT means the memory is free.
     * 
     * 
     * @return True for zero size. 
     */
    virtual bool isEmpty() const;
    
    virtual void printList() const;

    /** 
     * Just set the size to zero, will not release the memory.
     * 
     */
    virtual void makeEmpty();

    /** 
     * Return the position of the first element equals to @p _obj .
     * Return -1 if not exists.
     * 
     * @param _obj The value to find.
     * 
     * @return The position number.
     */
    virtual int find(const T& _obj) const;

    /** 
     * Insert a new element contained value @p _obj at position @p _p .
     * 
     * 
     * @param _p The positon to insert.
     * @param _obj The value for the inserted element.
     */
    virtual void insert(int _p, const T& _obj);

    /** 
     * Remove the element at positon @p _p .
     * 
     * @param _p The positon to remove.
     */
    virtual void remove(int _p);
};

/// The const limitation has been convergent to other functions. For
/// example, if we change warning to exception, and allow the funtion
/// to abort the program, then not only it self can not accept the
/// const limitation, but also all functions calling it.
template <typename T>
void Array<T>::warning(int _err) const
{
    switch (_err)
    {
    case 1:
	std::cerr << "Out of range." << std::endl;
	break;
    default:
	std::cerr << "Unexcepted error!" << std::endl;
	break;
    };
};

template <typename T>
void Array<T>::copyMemory(const T* _s, T* _d, int _n)
{
    for (int i = 0; i < _n; i++)
	_d[i] = _s[i];
};

template <typename T>
Array<T>::Array(const Array<T>& _obj)
{
    int n = _obj.size;
    elements = new T [n];
    copyMemory(_obj.elements, elements, n);    
    size = n;
};

template <typename T>
const Array<T>& Array<T>::operator=(const Array<T>& _obj)
{
    int n = _obj.size;
    if (elements != nullptr)
	delete [] elements;
    elements = new T [n];
    copyMemory(_obj.elements, elements, n);    
    size = n;
    return *this;
};

template <typename T>
bool Array<T>::isEmpty() const
{
    if (size == 0)
	return true;
    else
	return false;
};

template <typename T>
void Array<T>::printList() const
{
    if (isEmpty())
	return;
    for (int i = 0; i < size; i++)
	std::cout << elements[i] << ", ";
    std::cout << "\b\b" << std::endl;
};

template <typename T>
void Array<T>::makeEmpty()
{
    size = 0;
};

template <typename T>
int Array<T>::find(const T& _obj) const
{
    int i = 0;
    bool found = false;
    for (; i < size; i++)
	if (elements[i] == _obj)
	{
	    found = true;
	    break;
	}
    if (found == true)
	return i;
    else
	return -1;		/**< Should return nil, using -1 instead temporarily. */
};

template <typename T>
void Array<T>::insert(int _p, const T& _obj)
{
    if (_p < 0 || _p > size)
    {
	warning(1);
	return;
    }
    /// Careful!
    T* temp_data = new T [size + 1];
    copyMemory(elements, temp_data, _p);
    temp_data[_p] = _obj;
    copyMemory(elements + _p, temp_data + _p + 1, size - _p);
    delete [] elements;
    elements = temp_data;
    size++;
};

template <typename T>
void Array<T>::remove(int _p)
{
    if (_p < 0 || _p > size - 1)
    {
	warning(1);
	return;
    }
    /// Same as insert, it's an O(n) procedure!
    copyMemory(elements + _p + 1, elements + _p, size - _p - 1);
    size--;
};

#else
/// DO NOTHING!
#endif
